package migrate

import (
	"gorm.io/gorm"
	"io/ioutil"
	"wskyygyso/api/pkg/console"
	"wskyygyso/api/pkg/database"
	"wskyygyso/api/pkg/file"
)

// Migrator 数据迁移操作类
type Migrator struct {
	Folder   string
	DB       *gorm.DB
	Migrator gorm.Migrator
}

// Migration 对应数据的 migrations 表里的一条数据
type Migration struct {
	ID        uint64 `gorm:"primaryKey;autoIncrement;"`
	Migration string `gorm:"type:varchar(255);not null;unique;"`
	Batch     int
}

// NewMigrator 创建 Migrator 实例，用以执行迁移操作
func NewMigrator() *Migrator {
	// 初始化必要属性
	migrator := &Migrator{
		Folder:   "database/migrations",
		DB:       database.DB,
		Migrator: database.DB.Migrator(),
	}
	//创建migration表
	migrator.createMigrationsTable()
	return migrator
}

//createMigrationsTable 创建migration表
func (m *Migrator) createMigrationsTable() {
	migration := Migration{}
	if !m.Migrator.HasTable(&migration) {
		m.Migrator.CreateTable(&migration)
	}
}

//readAllMigrationFiles 获取所有的迁移文件
func (m *Migrator) readAllMigrationFiles() []MigrationFile {
	files, err := ioutil.ReadDir(m.Folder)
	console.ExitIf(err)
	var migrateFiles []MigrationFile
	for _, f := range files {
		fileName := file.FileNameWithoutExtension(f.Name())
		mfile := getMigrationFile(fileName)
		if len(mfile.FileName) > 0 {
			migrateFiles = append(migrateFiles, mfile)
		}
	}
	return migrationFiles
}

//getBatch 获取批次
func (m *Migrator) getBatch() int {
	batch := 1
	lastMigration := Migration{}
	m.DB.Order("id desc").First(&lastMigration)
	if lastMigration.ID > 0 {
		batch = lastMigration.Batch + 1
	}
	return batch
}

//runUpMigration 迁移文件
func (m Migrator) runUpMigration(mfile MigrationFile, batch int) {
	if mfile.Up != nil {
		// 友好提示
		console.Warning("migrating " + mfile.FileName)
		// 执行 up 方法
		mfile.Up(database.DB.Migrator(), database.SQLDB)
		// 提示已迁移了哪个文件
		console.Success("migrated " + mfile.FileName)
	}
	err := m.DB.Create(&Migration{
		Migration: mfile.FileName,
		Batch:     batch,
	}).Error
	console.ExitIf(err)
}

//Up 迁移
func (m *Migrator) Up() {
	migrateFiles := m.readAllMigrationFiles()
	batch := m.getBatch()
	migrations := []Migration{}
	m.DB.Find(&migrations)
	runed := false
	for _, mfile := range migrateFiles {
		if mfile.isNotMigrated(migrations) {
			m.runUpMigration(mfile, batch)
			runed = true
		}
	}
	if !runed {
		console.Success("database is up to date.")
	}
}

//Rollback 回滚
func (m *Migrator) Rollback() {
	lastMigration := Migration{}
	m.DB.Order("id desc").First(&lastMigration)
	migrations := []Migration{}
	m.DB.Order("id desc").Where("batch=?", lastMigration.Batch).Find(&migrations)
	if !m.rollbackMigrations(migrations) {
		console.Success("[migrations] table is empty, nothing to rollback.")
	}
}

//rollbackMigrations 回滚迁移文件
func (m *Migrator) rollbackMigrations(migrations []Migration) bool {
	runed := false
	for _, _migration := range migrations {
		console.Warning("rollback" + _migration.Migration)
		mfile := getMigrationFile(_migration.Migration)
		if mfile.Down != nil {
			mfile.Down(database.DB.Migrator(), database.SQLDB)
		}
		runed = true
		m.DB.Delete(&_migration)
		console.Success("finsh " + mfile.FileName)
	}
	return runed
}

//Reset 回滚所有迁移
func (m *Migrator) Reset() {
	migeations := []Migration{}
	m.DB.Order("id desc").Find(&migeations)
	if !m.rollbackMigrations(migeations) {
		console.Success("[migrations] table is empty, nothing to rollback.")
	}
}

//Refresh 回滚所有迁移，并运行所有迁移
func (m *Migrator) Refresh() {
	m.Reset()
	m.Up()
}

func (m *Migrator) Fresh() {
	dbName := database.CurrentDatabase()
	err := database.DeleteAllTables()
	console.ExitIf(err)
	console.Success("clear up database " + dbName)
	m.createMigrationsTable()
	console.Success("created migration")
	m.Up()
}
